package com.example.test.single;

/**
 * @author 晓果冻
 * @version 1.0
 * @date 2021/10/21 21:47
 */
public class SingletonLazy {

    public static SingletonLazy instance;

    /**
     * 构造函数私有化，外界不能通过new创建该对象
     */
    private SingletonLazy() {
    }

    /**
     * 对外只提供一个创建实例的方法，多线程下不安全
     *
     * @return
     */
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }

    /**
     * 通过synchronized枷锁保证多线程下的单例
     * 但synchronized开销大，又是在方法级别上控制的
     *
     * @return
     */
    public static synchronized SingletonLazy getInstance02() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }


    /**
     * 这是否安全，instance = new SingletonLazy(); 并不是原子性操作
     * 1、分配空间给对象
     * 2、在空间内创建对象
     * 3、将对象赋值给引用instance
     * <p>
     * 假如线程 1-》3-》2顺序，会把值写会主内存，其他线程就会读取到instance最新的值，但是这个是不完全的对象
     * (指令重排)
     *
     * @return
     */
    public static SingletonLazy getInstance03() {
        if (instance == null) {
            //假设此时同时有A、B线程满足到达这里
            synchronized (SingletonLazy.class) {
                //加锁 控制只有一个线程占有此锁
                if (instance == null) {
                    //涉及双重锁检查，如果现在堆中分配了空间，堆是线程共享的，所以其他线程都可以读到此空间内容
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

    /**
     * volatile是Java提供的关键字，它具有可见性和有序性，
     * 指令重排序是JVM对语句执行的优化，只要语句间没有依赖，那JVM就有权对语句进行优化
     * 禁止了指令重排
     */
    private static volatile SingletonLazy volatileInstance01;

    public static SingletonLazy getInstance04() {
        //第一重检查
        if (volatileInstance01 == null) {
            // A、B ，锁定
            synchronized (SingletonLazy.class) {
                //第二重检查
                if (volatileInstance01 == null) {
                    volatileInstance01 = new SingletonLazy();
                }
            }
        }
        return volatileInstance01;
    }
}
